////////////////////////////////////////////////
// DESCRIPTION:
//    Windows CE Registry access classes  
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#include <windows.h>
#include <devload.h>
#include <winerror.h>
#include <pkfuncs.h>
#include <nkintr.h>
#include <RESMGR.H>
#include <FusionWare\DFX.h>
#include <auto_xxx.hxx>

using namespace FusionWare;
using namespace FusionWare::Win32;

bool RegKey::Close()
{ 
    bool retVal = TRUE;

    if(this->hKey!=NULL)
    {
        DWORD status = RegCloseKey(this->hKey);
        SetLastError(status);
        retVal = (ERROR_SUCCESS == status);
        this->hKey=NULL;
    }

    return retVal;
}

bool RegKey::ReadValues(const RegValueEntry* pEntries, BYTE* pBase)
{
    DWORD maxValueLen;
    const RegValueEntry* pEntry;

    // allocate space for the largest value
    RegQueryInfoKey(this->hKey,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,&maxValueLen,NULL,NULL);
    ce::auto_ptr<BYTE> pValue=(BYTE*)malloc(maxValueLen);
    if(pValue == NULL)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return false;
    }

    // loop through and read all the values
    for(pEntry = pEntries; pEntry->ValueSize!=0 || pEntry->RegType==REGENTRY_CHAIN; ++pEntry)
    {
        if(pEntry->RegType==REGENTRY_CHAIN)
            ReadValues(pEntry->pChain,pBase);
        else
        {
            DWORD regType,valLen=maxValueLen;

            // read the value and save the status for possible errror message
            DWORD status = RegQueryValueEx(this->hKey,pEntry->ValueName,NULL,&regType,pValue,&valLen);
            SetLastError(status);
            BOOL valOK = (ERROR_SUCCESS == status); 
    
            // Check for conditions that should return a fail
            if(pEntry->Required && (!valOK || regType!=pEntry->RegType || valLen>pEntry->ValueSize))  
            {
                ERRORMSG(1,(_T("RegQueryValueEx(this->hKey,%s) Failed (0x%X)\r\n"),pEntry->ValueName,status));
                return false;
            }
    
            // only copy values actually read in
            if(valOK)
               memcpy(pBase + pEntry->OffsetValue,pValue,valLen);
        }
    }

    return true;
}

DWORD RegKey::QueryValueSize(const TCHAR* ValueName)
{
    DWORD regType;
    DWORD valSize=0;

    DWORD status = RegQueryValueEx(this->hKey, ValueName, NULL, &regType, NULL, &valSize);
    SetLastError(status);
    if(ERROR_SUCCESS!=status)
        return 0;

    return valSize;
}

bool RegKey::ReadValue(const TCHAR* ValueName, void* pData, DWORD& cbData)
{
    DWORD regType;

    DWORD status = RegQueryValueEx(this->hKey, ValueName, NULL, &regType, (BYTE*)pData, &cbData);
    SetLastError(status);
    if(ERROR_SUCCESS!=status)
        return FALSE;

    return TRUE;
}

bool RegKey::ReadValue(const TCHAR* ValueName, TCHAR* pData, DWORD& cbData)
{
    DWORD regType;

    DWORD status = RegQueryValueEx(this->hKey, ValueName, NULL, &regType, (BYTE*)pData, &cbData);
    SetLastError(status);
    if(ERROR_SUCCESS!=status)
        return FALSE;

    ASSERT(REG_SZ == regType || REG_MULTI_SZ == regType);
    return TRUE;
}

// {Internal} inline wrapper around RegEnumKeyEx to simplify the code
inline LONG RegEnumKey(HKEY hKey, DWORD dwIndex, LPTSTR lpName, DWORD cchName)
{
    DWORD status = RegEnumKeyEx(hKey,dwIndex,lpName,&cchName,NULL,NULL,NULL,NULL);
    SetLastError(status);
    return status;
}


// Read a DWORD from the registry
// Won't override the data pointed to by pData unless a valid value exists in the registry
// this allows for setting up of default values first and calling this function
// to check for an override.
bool RegKey::ReadValue(const TCHAR* Name,DWORD* pData)
{
    DWORD tempVal;
    DWORD regType;
    DWORD sz=sizeof(tempVal);

    DWORD status = RegQueryValueEx(this->hKey, Name, NULL, &regType, (BYTE*)&tempVal, &sz);
    SetLastError(status);    
    // Don't modify caller provided data unless good value in registry
    if(status!=ERROR_SUCCESS || regType != REG_DWORD || sz != sizeof(DWORD))
        return FALSE;

    *pData = tempVal;
    return TRUE;
}

bool RegKey::WriteValue(const TCHAR* ValueName, const TCHAR* pData, DWORD cbData, bool IsMultiSZ)
{
    DWORD regType = IsMultiSZ ? REG_MULTI_SZ : REG_SZ;
    
    // Make sure that a size is given for MULTI_SZ values as _tcslen won't do
    ASSERT(!IsMultiSZ || cbData!=0);
    if(0==cbData)
    {
       cbData = _tcslen(pData);
       regType = REG_SZ; 
    }

    DWORD status = RegSetValueEx(this->hKey, ValueName, 0, regType, (const BYTE*)pData, cbData);
    SetLastError(status);
    if(ERROR_SUCCESS!=status)
        return FALSE;

    ASSERT(REG_SZ == regType || REG_MULTI_SZ == regType);
    return TRUE;
}

